package com.chunuo.configuration.starter;

import com.chunuo.configuration.exception.ChunuoException;
import com.chunuo.configuration.factory.JdbcUtils;
import com.chunuo.configuration.factory.PropertiesFile;
import com.chunuo.configuration.utils.AnnotationUtils;
import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * 初始化配置
 *
 * @author liujiamiao
 * @email 2544296058@qq.com
 * @date 2020-12-22 12:45
 */
public class AutoConfiguration {

    // 存储扫描包、dao、service（事务）等等
    private List<String> scans = new ArrayList<>();

    // 真正操作ioc容器的类
    public ComponentAutoConfiguration component = null;

    private static AutoConfiguration autoConfiguration = null;

    public static AutoConfiguration newInstance() {
        if(autoConfiguration==null){
            synchronized (AutoConfiguration.class){
                if(autoConfiguration==null){
                    autoConfiguration = new AutoConfiguration();
                }
            }
        }
        return autoConfiguration;
    }

    private AutoConfiguration() {
        //初始化基本配置，properties...
        PropertiesFile propertiesFile = PropertiesFile.newInstance();
        String packageNameScan = String.valueOf(propertiesFile.get("packageNameScan"));
        String constructingMode = String.valueOf(propertiesFile.get("constructingMode"));
        String mapperScan = String.valueOf(propertiesFile.get("mapperScan"));
        String entityScan = String.valueOf(propertiesFile.get("entityScan"));
        String serviceScan = String.valueOf(propertiesFile.get("serviceScan"));
        //初始化连接池
        JdbcUtils.create(propertiesFile);

        //common和autowired注解

        //将路径（.）换成  \(File.separator)
        packageNameScan = packageNameScan.replace(".", File.separator);
        System.out.println("项目扫描的包 -> "+packageNameScan);

        //得到要扫描的包（绝对路径）
        String path = AnnotationUtils.getPath(packageNameScan);
        path = path.replace("test-classes", "classes");
        File searchPath = new File(path);
        //绝对路径 - 包路径
        System.out.println("项目的启动路径 -> "+searchPath);

        //不为空都添加到scans中
        if(packageNameScan!=null&&!packageNameScan.isEmpty()){
            scans.add(packageNameScan);
        }
        if(mapperScan!=null&&!mapperScan.isEmpty()){
            scans.add(mapperScan);
        }
        if(serviceScan!=null&&!serviceScan.isEmpty()){
            scans.add(serviceScan);
        }


        //将扫描好的注解类和注解方法，将其反射创建和注入
//        component = new ComponentAutoConfiguration(searchPath, scans, Arrays.asList(Component.class, Autowired.class,RequestMapping.class));
        component = new ComponentAutoConfiguration(searchPath, scans);
        //对象容器和属性容器
        showMap(component.getObjectMap());
        showMap(component.getFieldMap());
        showMap(component.getRequestMap());
        System.out.println();
    }


    /**
     * 映射到方法上
     * @param uri
     * @param requestMethod 请求方式
     * @param argsMap
     */
    public Object service(String uri,String requestMethod, Map<String,Object> argsMap,String body) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException {
        //根据地址拿到 对象key_方法名_请求方式
        String value = component.getRequestMap().get(uri);
        if(value==null){
            //当路径匹配不到的话，可能是使用到了@PathVariable注解，所以得遍历所有接口
            for(Map.Entry<String,String> entry: component.getRequestMap().entrySet()){
                String key = entry.getKey();
                //查看该接口是否有{
                int left = key.indexOf("{");
                if(left>=0){
                    //list{id} 剪切{id}前半段 等于list
                    String tagUri = key.substring(0, left);
                    //路径一致
                    if(uri.indexOf(tagUri)==0){
                        //截取后半段
                        String lastPath = key.substring(left);
                        String lastUri = uri.substring(left);
                        System.out.println(lastPath +"  =  "+lastUri);// {id}  =  abc
                        //拆分
                        String[] pathSplit = lastPath.split("/");
                        String[] uriSplit = lastUri.split("/");
//                        System.out.println(Arrays.toString(pathSplit));//[{id}, {page}, {limit}]
//                        System.out.println(Arrays.toString(uriSplit));//[abc, 1, 10]
                        //判断参数个数是否一致
                        if(pathSplit.length==uriSplit.length){
                            //比较
                            if(compare(pathSplit,uriSplit)){
                                //确保一致后，将参数添加到argsMap集合中
                                for(int i = 0;i<pathSplit.length;i++){
                                    String pathVariable = pathSplit[i].trim();
                                    argsMap.put(pathVariable.substring(1,pathVariable.length()-1).trim(),uriSplit[i]);
                                }
                                //在把该对象的key赋值给value
                                value = entry.getValue();
                            }
                        }
                    }
                }
            }
            if(value==null){
                throw new  RuntimeException("请求地址为null "+uri);
            }
        }
        String[] split = value.split("_");

        //是否由请求要求（post、get。。。）
        if(!split[2].isEmpty()){
            //请求方式是否一样
            if(!requestMethod.equalsIgnoreCase(split[2])){
                throw new RuntimeException("请求方式错误，要求 "+split[2]+"请求方式，结果收到的是"+requestMethod);
            }
        }

        //拿到对象
        Object o = component.getObjectMap().get(split[0]);

        //结构 =》 javax.servlet.http.HttpServletRequest-[];javax.servlet.http.HttpServletResponse-[];
        String[] params = split[3].split(";");//划分参数
        System.out.println("参数 = "+Arrays.toString(params));
        //请求参数的class
        Class[] clazzs = new Class[params.length];
        Object[] args = new Object[params.length];
        if(params.length>0){
            for(int i = 0;i<params.length;i++){
                //结构 =》  javax.servlet.http.HttpServletRequest-[]
                String[] classAnnotation = params[i].split("-");
                //如果参数中有HttpServletRequest和HttpServletResponse，则要给他添加该参数
                if("javax.servlet.http.HttpServletRequest".equals(classAnnotation[0])){
                    clazzs[i] = HttpServletRequest.class;
                    args[i] = argsMap.get(classAnnotation[0]);
                }else if("javax.servlet.http.HttpServletResponse".equals(classAnnotation[0])){
                    clazzs[i] = HttpServletResponse.class;
                    args[i] = argsMap.get(classAnnotation[0]);
                }else{
                    //有注解（左右两边[]占据两个长度） 结构 =》  [@com.chunuo.configuration.annotation.RequestParam(value=id)]
                    if(classAnnotation[1].length()>2){

                        //拿到注解
                        String[] annotations = classAnnotation[1].substring(1, classAnnotation[1].length() - 1).split(",");
                        for(String annotation:annotations){
                            // @com.chunuo.configuration.annotation.RequestParam(value=id)
                            annotation = annotation.trim();
                            if(annotation.indexOf("@com.chunuo.configuration.annotation.RequestParam")>=0||
                                annotation.indexOf("@com.chunuo.configuration.annotation.PathVariable")>=0){
                                //只取这个注解的值
                                //参数接收的类型
                                clazzs[i] = Class.forName(classAnnotation[0]);
                                //获取注解的value
                                String annotationName = AnnotationUtils.getAnnotationName(annotation);
                                //得到参数值（类型绝对是String，可能和接口参数接收不一致）
                                args[i] = argsMap.get(annotationName);
                                if(args[i]==null){
                                    throw new ChunuoException("请求参数为null 访问"+uri+"接口，没有"+annotationName+"参数");
                                }

                                //如果不一致
                                if(!clazzs[i].toString().equals(args[i].getClass().toString())){
                                    System.out.println("接口参数类型是否一致 "+clazzs[i].toString()+"   =   "+args[i].getClass());
                                    try {
                                        if("class java.lang.Integer".equals(clazzs[i].toString())){
                                            args[i] = Integer.parseInt(String.valueOf(args[i]));
                                        }else if("class java.lang.Short".equals(clazzs[i].toString())){
                                            args[i] = Short.parseShort(String.valueOf(args[i]));
                                        }else if("class java.lang.Byte".equals(clazzs[i].toString())){
                                            args[i] = Byte.parseByte(String.valueOf(args[i]));
                                        }else if("class java.lang.Long".equals(clazzs[i].toString())){
                                            args[i] = Long.parseLong(String.valueOf(args[i]));
                                        }else if("class java.lang.Float".equals(clazzs[i].toString())){
                                            args[i] = Float.parseFloat(String.valueOf(args[i]));
                                        }else if("class java.lang.Double".equals(clazzs[i].toString())){
                                            args[i] = Double.parseDouble(String.valueOf(args[i]));
                                        }else if("class java.lang.Boolean".equals(clazzs[i].toString())){
                                            args[i] = Integer.parseInt(String.valueOf(args[i]));
                                        }else if("class java.lang.Character".equals(clazzs[i].toString())){
                                            args[i] = new Character((char)Integer.parseInt(String.valueOf(args[i])));
                                        }
                                    } catch (NumberFormatException e) {
                                        throw new RuntimeException("接口类型匹配错误，期待接收的类型为"+ clazzs[i].toString() +"，结果接收的类型为"+args[i].getClass().toString()+"，值 = “"+args[i]+"”  请检查！");
                                    }
                                }
                                if(args[i]==null){
                                    throw new RuntimeException(annotationName+"该参数没有找到！");
                                }

                            }else if(annotation.indexOf("@com.chunuo.configuration.annotation.RequestBody()")>=0){
                                if(body==null){
                                    throw new ChunuoException("请求体为null，请检查！");
                                }
                                //请求体
                                ObjectMapper mapper = new ObjectMapper();
                                try {
                                    Object paramBody = mapper.readValue(body, Class.forName(classAnnotation[0]));
                                    args[i] = paramBody;
                                    clazzs[i] = paramBody.getClass();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }else{
                        throw new RuntimeException("参数没有加注解！"+split[1]+"方法的"+classAnnotation[0]);
                    }
                }

            }
        }

        //选择哪个方法
        Method method = o.getClass().getDeclaredMethod(split[1],clazzs);
        //调用方法
        return method.invoke(o, args);
    }

    public boolean compare(String[] pathSplit,String[] uriSplit){
        for(int i = 0;i<pathSplit.length;i++){
            if(!(!pathSplit[i].trim().isEmpty()&&!uriSplit[i].trim().isEmpty())){
                System.out.println("存在为空的 "+pathSplit[i].trim()+" : "+uriSplit[i].trim());
                return false;
            }
        }
        return true;
    }

    private <T,R> void showMap(Map<T,R> map){
        for(Map.Entry<T,R> entry : map.entrySet()){
            System.out.println(entry.getKey() +"  ---  "+entry.getValue());
        }
        System.out.println();
    }

    public <T> T getBean(Class<T> clazz) {
        Object obj = component.getObjectMap().get(clazz.toString().split(" ")[1]);
        return (T)obj;
    }

    public <T> T getBean(String name){
        Object obj = component.getObjectMap().get(name);
        return (T) obj;
    }

}
